home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / HTTP / Client.php next >
Encoding:
PHP Script  |  2005-12-02  |  14.1 KB  |  462 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at                              |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Alexey Borzov <avb@php.net>                                  |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Client.php,v 1.4 2004/03/23 13:35:37 avb Exp $
  20.  
  21. require_once 'HTTP/Request.php';
  22. require_once 'HTTP/Client/CookieManager.php';
  23.  
  24. /**
  25.  * A simple HTTP client class.
  26.  * 
  27.  * The class wraps around HTTP_Request providing a higher-level
  28.  * API for performing multiple HTTP requests
  29.  * 
  30.  * @package HTTP_Client
  31.  * @author Alexey Borzov <avb@php.net>
  32.  * @version $Revision: 1.4 $
  33.  */
  34. class HTTP_Client
  35. {
  36.    /**
  37.     * An HTTP_Client_CookieManager instance
  38.     * @var object
  39.     */
  40.     var $_cookieManager;
  41.  
  42.    /**
  43.     * Received HTTP responses
  44.     * @var array
  45.     */
  46.     var $_responses;
  47.  
  48.    /**
  49.     * Default headers to send on every request
  50.     * @var array
  51.     */
  52.     var $_defaultHeaders = array();
  53.  
  54.    /**
  55.     * Default parameters for HTTP_Request's constructor
  56.     * @var array
  57.     */
  58.     var $_defaultRequestParams = array();
  59.  
  60.    /**
  61.     * How many redirects were done
  62.     * @var integer
  63.     */
  64.     var $_redirectCount = 0;
  65.  
  66.    /**
  67.     * Maximum allowed redirects
  68.     * @var integer
  69.     */
  70.     var $_maxRedirects = 5;
  71.  
  72.    /**
  73.     * Listeners attached to the client  
  74.     * @var array
  75.     */
  76.     var $_listeners = array();
  77.  
  78.    /**
  79.     * Whether the listener should be propagated to Request objects
  80.     * @var array
  81.     */
  82.     var $_propagate = array();
  83.  
  84.    /**
  85.     * Whether to keep all the responses or just the most recent one
  86.     * @var boolean
  87.     */
  88.     var $_isHistoryEnabled = true;
  89.  
  90.    /**
  91.     * Constructor
  92.     * 
  93.     * @access   public
  94.     * @param    array   Parameters to pass to HTTP_Request's constructor
  95.     * @param    array   Default headers to send on every request
  96.     */
  97.     function HTTP_Client($defaultRequestParams = null, $defaultHeaders = null)
  98.     {
  99.         $this->_cookieManager =& new HTTP_Client_CookieManager();
  100.         if (isset($defaultHeaders)) {
  101.             $this->setDefaultHeader($defaultHeaders);
  102.         }
  103.         if (isset($defaultRequestParams)) {
  104.             $this->setRequestParameter($defaultRequestParams);
  105.         }
  106.     }
  107.  
  108.  
  109.    /**
  110.     * Sets the maximum redirects that will be processed.
  111.     * 
  112.     * Setting this to 0 disables redirect processing. If not 0 and the 
  113.     * number of redirects in a request is bigger than this number, then an
  114.     * error will be raised.
  115.     * 
  116.     * @access   public
  117.     * @param    int     Max number of redirects to process
  118.     */
  119.     function setMaxRedirects($value)
  120.     {
  121.         $this->_maxRedirects = $value;
  122.     }
  123.  
  124.  
  125.    /**
  126.     * Sets whether to keep all the responses or just the most recent one
  127.     *
  128.     * @access public
  129.     * @param  bool      Whether to enable history
  130.     */
  131.     function enableHistory($enable)
  132.     {
  133.         $this->_isHistoryEnabled = (bool)$enable;
  134.     }
  135.  
  136.    /**
  137.     * Creates a HTTP_Request objects, applying all the necessary defaults
  138.     *
  139.     * @param    string   URL
  140.     * @param    integer  Method, constants are defined in HTTP_Request
  141.     * @access   private
  142.     * @return   object   HTTP_Request object with all defaults applied
  143.     */
  144.     function &_createRequest($url, $method = HTTP_REQUEST_METHOD_GET)
  145.     {
  146.         $req =& new HTTP_Request($url, $this->_defaultRequestParams);
  147.         $req->setMethod($method);
  148.         foreach ($this->_defaultHeaders as $name => $value) {
  149.             $req->addHeader($name, $value);
  150.         }
  151.         $this->_cookieManager->passCookies($req);
  152.         foreach ($this->_propagate as $id => $propagate) {
  153.             if ($propagate) {
  154.                 $req->attach($this->_listeners[$id]);
  155.             }
  156.         }
  157.         return $req;
  158.     }
  159.     
  160.  
  161.    /**
  162.     * Sends a 'HEAD' HTTP request
  163.     *
  164.     * @param    string  URL
  165.     * @access   public
  166.     * @return   integer HTTP response code
  167.     * @throws   PEAR_Error
  168.     */
  169.     function head($url)
  170.     {
  171.         $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_HEAD);
  172.         return $this->_performRequest($request);
  173.     }
  174.    
  175.  
  176.    /**
  177.     * Sends a 'GET' HTTP request
  178.     * 
  179.     * @param    string  URL
  180.     * @param    mixed   additional data to send
  181.     * @param    boolean Whether the data is already urlencoded
  182.     * @access   public
  183.     * @return   integer HTTP response code
  184.     * @throws   PEAR_Error
  185.     */
  186.     function get($url, $data = null, $preEncoded = false)
  187.     {
  188.         $request =& $this->_createRequest($url);
  189.         if (is_array($data)) {
  190.             foreach ($data as $name => $value) {
  191.                 $request->addQueryString($name, $value, $preEncoded);
  192.             }
  193.         } elseif (isset($data)) {
  194.             $request->addRawQueryString($data, $preEncoded);
  195.         }
  196.         return $this->_performRequest($request);
  197.     }
  198.  
  199.  
  200.    /**
  201.     * Sends a 'POST' HTTP request
  202.     *
  203.     * @param    string  URL
  204.     * @param    mixed   Data to send
  205.     * @param    boolean Whether the data is already urlencoded
  206.     * @param    array   Files to upload. Elements of the array should have the form:
  207.     *                   array(name, filename(s)[, content type]), see HTTP_Request::addFile()
  208.     * @access   public
  209.     * @return   integer HTTP response code
  210.     * @throws   PEAR_Error
  211.     */
  212.     function post($url, $data, $preEncoded = false, $files = array())
  213.     {
  214.         $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_POST);
  215.         if (is_array($data)) {
  216.             foreach ($data as $name => $value) {
  217.                 $request->addPostData($name, $value, $preEncoded);
  218.             }
  219.         } else {
  220.             $request->addRawPostData($data, $preEncoded);
  221.         }
  222.         foreach ($files as $fileData) {
  223.             $res = call_user_func_array(array(&$request, 'addFile'), $fileData);
  224.             if (PEAR::isError($res)) {
  225.                 return $res;
  226.             }
  227.         }
  228.         return $this->_performRequest($request);
  229.     }
  230.  
  231.  
  232.    /**
  233.     * Sets default header(s) for HTTP requests
  234.     *
  235.     * @param    mixed   header name or array ('header name' => 'header value')
  236.     * @param    string  header value if $name is not an array
  237.     * @access   public
  238.     */
  239.     function setDefaultHeader($name, $value = null)
  240.     {
  241.         if (is_array($name)) {
  242.             $this->_defaultHeaders = array_merge($this->_defaultHeaders, $name);
  243.         } else {
  244.             $this->_defaultHeaders[$name] = $value;
  245.         }
  246.     }
  247.  
  248.  
  249.    /**
  250.     * Sets parameter(s) for HTTP requests
  251.     *
  252.     * @param    mixed   parameter name or array ('parameter name' => 'parameter value')
  253.     * @param    string  parameter value if $name is not an array
  254.     * @access   public
  255.     */
  256.     function setRequestParameter($name, $value = null)
  257.     {
  258.         if (is_array($name)) {
  259.             $this->_defaultRequestParams = array_merge($this->_defaultRequestParams, $name);
  260.         } else {
  261.             $this->_defaultRequestParams[$name] = $value;
  262.         }
  263.     }
  264.       
  265.  
  266.    /**
  267.     * Performs a request, processes redirects
  268.     *
  269.     * @param    object  HTTP_Request object
  270.     * @access   private
  271.     * @return   integer HTTP response code
  272.     * @throws   PEAR_Error
  273.     */
  274.     function _performRequest(&$request)
  275.     {
  276.         // If this is not a redirect, notify the listeners of new request
  277.         if (0 == $this->_redirectCount) {
  278.             $this->_notify('request', $request->_url->getUrl());
  279.         }
  280.         if (PEAR::isError($err = $request->sendRequest())) {
  281.             return $err;
  282.         }
  283.         $this->_pushResponse($request);
  284.  
  285.         $code = $request->getResponseCode();
  286.         if ($this->_maxRedirects > 0 && in_array($code, array(300, 301, 302, 303, 307))) {
  287.             if (++$this->_redirectCount > $this->_maxRedirects) {
  288.                 return PEAR::raiseError('Too many redirects');
  289.             }
  290.             $location = $request->getResponseHeader('Location');
  291.             if ('' == $location) {
  292.                 return PEAR::raiseError("No 'Location' field on redirect");
  293.             }
  294.             $url = $this->_redirectUrl($request->_url, $location);
  295.             // Notify of redirection
  296.             $this->_notify('httpRedirect', $url);
  297.             // we access the private properties directly, as there are no accessors for them
  298.             switch ($request->_method) {
  299.                 case HTTP_REQUEST_METHOD_POST: 
  300.                     if (302 == $code || 303 == $code) {
  301.                         return $this->get($url);
  302.                     } else {
  303.                         $postFiles = array();
  304.                         foreach ($request->_postFiles as $name => $data) {
  305.                             $postFiles[] = array($name, $data['name'], $data['type']);
  306.                         }
  307.                         return $this->post($url, $request->_postData, true, $postFiles);
  308.                     }
  309.                 case HTTP_REQUEST_METHOD_HEAD:
  310.                     return (303 == $code? $this->get($url): $this->head($url));
  311.                 case HTTP_REQUEST_METHOD_GET: 
  312.                 default:
  313.                     return $this->get($url);
  314.             } // switch
  315.  
  316.         } else {
  317.             $this->_redirectCount = 0;
  318.             if (400 >= $code) {
  319.                 $this->_notify('httpSuccess');
  320.                 $this->setDefaultHeader('Referer', $request->_url->getUrl());
  321.                 // some result processing should go here
  322.             } else {
  323.                 $this->_notify('httpError');
  324.             }
  325.         }
  326.         return $code;
  327.     }
  328.  
  329.  
  330.    /**
  331.     * Returns the most recent HTTP response
  332.     * 
  333.     * @access public
  334.     * @return array
  335.     */
  336.     function ¤tResponse()
  337.     {
  338.         return $this->_responses[count($this->_responses) - 1];
  339.     }
  340.  
  341.  
  342.    /**
  343.     * Saves the server's response to responses list
  344.     *
  345.     * @param    object  HTTP_Request object, with request already sent
  346.     * @access   private
  347.     */
  348.     function _pushResponse(&$request)
  349.     {
  350.         $this->_cookieManager->updateCookies($request);
  351.         $idx   = $this->_isHistoryEnabled? count($this->_responses): 0;
  352.         $this->_responses[$idx] = array(
  353.             'code'    => $request->getResponseCode(),
  354.             'headers' => $request->getResponseHeader(),
  355.             'body'    => $request->getResponseBody()
  356.         );
  357.     }
  358.  
  359.  
  360.    /**
  361.     * Clears object's internal properties
  362.     *
  363.     * @access public
  364.     */
  365.     function reset()
  366.     {
  367.         $this->_cookieManager->reset();
  368.         $this->_responses            = array();
  369.         $this->_defaultHeaders       = array();
  370.         $this->_defaultRequestParams = array();
  371.     }
  372.  
  373.  
  374.    /**
  375.     * Adds a Listener to the list of listeners that are notified of
  376.     * the object's events
  377.     * 
  378.     * @param    object   HTTP_Request_Listener instance to attach
  379.     * @param    boolean  Whether the listener should be attached to the 
  380.     *                    created HTTP_Request objects
  381.     * @return   boolean  whether the listener was successfully attached
  382.     * @access   public
  383.     */
  384.     function attach(&$listener, $propagate = false)
  385.     {
  386.         if (!is_a($listener, 'HTTP_Request_Listener')) {
  387.             return false;
  388.         }
  389.         $this->_listeners[$listener->getId()] =& $listener;
  390.         $this->_propagate[$listener->getId()] =  $propagate;
  391.         return true;
  392.     }
  393.  
  394.  
  395.    /**
  396.     * Removes a Listener from the list of listeners 
  397.     * 
  398.     * @param    object   HTTP_Request_Listener instance to detach
  399.     * @return   boolean  whether the listener was successfully detached
  400.     * @access   public
  401.     */
  402.     function detach(&$listener)
  403.     {
  404.         if (!is_a($listener, 'HTTP_Request_Listener') || 
  405.             !isset($this->_listeners[$listener->getId()])) {
  406.             return false;
  407.         }
  408.         unset($this->_listeners[$listener->getId()], $this->_propagate[$listener->getId()]);
  409.         return true;
  410.     }
  411.  
  412.  
  413.    /**
  414.     * Notifies all registered listeners of an event.
  415.     * 
  416.     * Currently available events are:
  417.     * 'request': sent on HTTP request that is not a redirect
  418.     * 'httpSuccess': sent when we receive a successfull 2xx response
  419.     * 'httpRedirect': sent when we receive a redirection response
  420.     * 'httpError': sent on 4xx, 5xx response
  421.     * 
  422.     * @param    string  Event name
  423.     * @param    mixed   Additional data
  424.     * @access   private
  425.     */
  426.     function _notify($event, $data = null)
  427.     {
  428.         foreach (array_keys($this->_listeners) as $id) {
  429.             $this->_listeners[$id]->update($this, $event, $data);
  430.         }
  431.     }
  432.  
  433.  
  434.    /**
  435.     * Calculates the absolute URL of a redirect
  436.     *  
  437.     * @param    object  Net_Url object containing the request URL
  438.     * @param    string  Value of the 'Location' response header
  439.     * @return   string  Absolute URL we are being redirected to
  440.     * @access   private
  441.     */
  442.     function _redirectUrl($url, $location)
  443.     {
  444.         if (preg_match('!^https?://!i', $location)) {
  445.             return $location;
  446.         } else {
  447.             if ('/' == $location{0}) {
  448.                 $url->path = Net_URL::resolvePath($location);
  449.             } elseif('/' == substr($url->path, -1)) {
  450.                 $url->path = Net_URL::resolvePath($url->path . $location);
  451.             } else {
  452.                 $dirname = (DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path));
  453.                 $url->path = Net_URL::resolvePath($dirname . '/' . $location);
  454.             }
  455.             $url->querystring = array();
  456.             $url->anchor      = '';
  457.             return $url->getUrl();
  458.         }
  459.     }
  460. }
  461. ?>
  462.